package com.muyu.system.danmo.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.muyu.system.danmo.domain.PayOrder;
import com.muyu.system.danmo.domain.RefundOrder;
import com.muyu.system.danmo.domain.WechatPayConfig;
import com.muyu.system.danmo.dto.PayServiceDTO;
import com.muyu.system.danmo.dto.PaymentCallBackResult;
import com.muyu.system.danmo.dto.RefundServiceDTO;
import com.muyu.common.core.enums.PayChannel;
import com.muyu.common.core.enums.PayStatus;
import com.muyu.system.danmo.service.PayOrderService;
import com.muyu.system.danmo.service.RefundOrderService;
import com.muyu.system.danmo.vo.RefundResultServiceVO;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.partnerpayments.app.model.Transaction;
import com.wechat.pay.java.service.payments.app.AppServiceExtension;
import com.wechat.pay.java.service.payments.app.model.*;
import com.muyu.system.danmo.service.PayChannelService;
import com.muyu.system.danmo.service.WechatPayConfigService;
import com.muyu.system.danmo.vo.PayResultVO;

import com.wechat.pay.java.service.payments.app.model.Amount;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

import static com.muyu.common.core.constant.TopicConstant.COMMON_TOPIC_NORMAL;
import static com.muyu.system.danmo.util.TagConstant.PAY_NOTIFY_TAG;
import static com.muyu.system.danmo.util.TagConstant.REFUND_PAY_NOTIFY_TAG;

@Slf4j
@Service("WeChatService")
@RequiredArgsConstructor
public class WeChatService implements PayChannelService {

    private final WechatPayConfigService wechatPayConfigService;

    @Value("${wechat_notify_url}")
    private String notifyUrl;


    @Value("${wechat_refund_notify_url}")
    private String refundNotifyUrl;

    private final Map<String, Config> configMap = new HashMap<>();

    private final Map<String, NotificationConfig> notifyMap = new HashMap<>();

    private final RocketMQTemplate rocketMQTemplate;

    private final PayOrderService payOrderService;

    private final RefundOrderService refundOrderService;

    @Override
    public PayResultVO pay(PayServiceDTO payServiceDTO, PayOrder payOrder) {
        log.info("进入微信支付：{} ==== {}", payServiceDTO, payOrder);
        // 获取微信支付配置
        WechatPayConfig wechatPayConfig = wechatPayConfigService.lambdaQuery().eq(WechatPayConfig::getMchCode, payServiceDTO.getMchCode()).one();
        PayResultVO payResultVO = new PayResultVO();
        payResultVO.setBizOrderNo(payOrder.getBizOrderNo());
        payResultVO.setOrderNo(payOrder.getOrderNo());
        switch (payServiceDTO.getPayMethod()){
            case WECHAT_APP:
                // 组装参数
                PrepayRequest prepayRequest = getPrepayRequest(payServiceDTO, payOrder, wechatPayConfig);
                // 发起请求
                String payInfo = appRequest(prepayRequest, wechatPayConfig, payServiceDTO.getMchCode());
                payResultVO.setPayInfo(payInfo);
                break;
            default:
                throw new RuntimeException("不支持的支付方式");
        }
        return payResultVO;
    }

    @Override
    public ResponseEntity.BodyBuilder payNotify(HttpServletRequest request, PayChannel payChannel, String mchCode) {
        RequestParam requestParam = getRequestParam(request);
        // 获取微信支付配置
        WechatPayConfig wechatPayConfig = wechatPayConfigService.lambdaQuery().eq(WechatPayConfig::getMchCode, mchCode).one();
        NotificationConfig notifyConfig = getNotifyConfig(mchCode, wechatPayConfig);
        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(notifyConfig);
        Transaction transaction = null;
        try {
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            transaction = parser.parse(requestParam, Transaction.class);
        } catch (RuntimeException e) {
            // 签名验证失败，返回 401 UNAUTHORIZED 状态码
            log.error("sign verification failed", e);
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED);
        }
        // 定义对应的时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;

        PaymentCallBackResult paymentCallBackResult = new PaymentCallBackResult();
        paymentCallBackResult.setOrderNo(transaction.getOutTradeNo());
        switch (transaction.getTradeState()){
            case SUCCESS:
                paymentCallBackResult.setStatus(PayStatus.SUCCESS);
                // 解析字符串为 ZonedDateTime
                ZonedDateTime dateTime = ZonedDateTime.parse(transaction.getSuccessTime(), formatter);
                paymentCallBackResult.setTransactionId(transaction.getTransactionId());
                paymentCallBackResult.setCompletedTime(dateTime.toLocalDateTime());
                paymentCallBackResult.setNotifyTime(LocalDateTime.now());
                paymentCallBackResult.setOpenId(transaction.getPayer().getSpOpenid());
                break;
            case NOTPAY:
                paymentCallBackResult.setStatus(PayStatus.PENDING);
                break;
            default:
                paymentCallBackResult.setStatus(PayStatus.FAIL);
        }
        paymentCallBackResult.setCode(transaction.getTradeState().toString());
        paymentCallBackResult.setMessage(transaction.getTradeStateDesc());
        // 获取业务方的订单号
        PayOrder one = payOrderService.lambdaQuery().eq(PayOrder::getOrderNo, paymentCallBackResult.getOrderNo()).select(PayOrder::getBizOrderNo).one();
        if (one != null) {
            paymentCallBackResult.setBizOrderNo(one.getBizOrderNo());
        }
        SendResult sendResult = rocketMQTemplate.syncSend(COMMON_TOPIC_NORMAL + ":" + PAY_NOTIFY_TAG, JSONUtil.toJsonStr(paymentCallBackResult));
        log.info("微信支付回调完整结果：{}== mq:{}", JSONUtil.toJsonStr(paymentCallBackResult),  sendResult.getMsgId());
        return ResponseEntity.status(HttpStatus.OK);
    }


    /**
     * 获取微信通知的信息
     * @param request
     * @return
     */
    private RequestParam getRequestParam(HttpServletRequest request){
        //获取报文
        String body = getRequestBody(request);
        //随机串
        String nonceStr = request.getHeader("Wechatpay-Nonce");
        //微信传递过来的签名
        String signature = request.getHeader("Wechatpay-Signature");
        //证书序列号（微信平台）
        String serialNo = request.getHeader("Wechatpay-Serial");
        //时间戳
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        // 构造 RequestParam
        return new RequestParam.Builder()
                .serialNumber(serialNo)
                .nonce(nonceStr)
                .signature(signature)
                .timestamp(timestamp)
                .body(body)
                .build();
    }




    @Override
    public ResponseEntity.BodyBuilder refundPayNotify(HttpServletRequest request, PayChannel payChannel, String mchCode) {
        RequestParam requestParam = getRequestParam(request);
        // 获取微信支付配置
        WechatPayConfig wechatPayConfig = wechatPayConfigService.lambdaQuery().eq(WechatPayConfig::getMchCode, mchCode).one();
        NotificationConfig notifyConfig = getNotifyConfig(mchCode, wechatPayConfig);
        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(notifyConfig);
        RefundNotification refundNotification = null;
        try {
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            refundNotification = parser.parse(requestParam, RefundNotification.class);
        } catch (RuntimeException e) {
            // 签名验证失败，返回 401 UNAUTHORIZED 状态码
            log.error("sign verification failed", e);
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED);
        }
        // 定义对应的时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;

        RefundResultServiceVO result = new RefundResultServiceVO();
        result.setRefundOrderNo(refundNotification.getOutRefundNo());
        switch (refundNotification.getRefundStatus()){
            case SUCCESS:
                result.setStatus(PayStatus.SUCCESS);
                // 解析字符串为 ZonedDateTime
                ZonedDateTime dateTime = ZonedDateTime.parse(refundNotification.getSuccessTime(), formatter);
                result.setTransactionId(refundNotification.getRefundId());
                result.setCompletedTime(dateTime.toLocalDateTime());
                break;
            case PROCESSING:
                result.setStatus(PayStatus.PENDING);
                break;
            default:
                result.setStatus(PayStatus.FAIL);
        }
        result.setPayChannel(PayChannel.WECHAT);
        // 获取业务方的订单号
        RefundOrder one = refundOrderService.lambdaQuery().eq(RefundOrder::getRefundOrderNo, result.getRefundOrderNo()).select(RefundOrder::getRefundBizOrderNo).one();
        if (one != null) {
            result.setRefundBizOrderNo(one.getRefundBizOrderNo());
        }
        SendResult sendResult = rocketMQTemplate.syncSend(COMMON_TOPIC_NORMAL + ":" + REFUND_PAY_NOTIFY_TAG, JSONUtil.toJsonStr(result));
        log.info("微信退款回调完整结果：{}== mq:{}", JSONUtil.toJsonStr(result),  sendResult.getMsgId());
        return ResponseEntity.status(HttpStatus.OK);
    }


    @Override
    public PaymentCallBackResult queryOrder(String mchCode, String orderNo) {
        // 获取微信支付配置
        WechatPayConfig wechatPayConfig = wechatPayConfigService.lambdaQuery().eq(WechatPayConfig::getMchCode, mchCode).one();
        // 获取请求配置
        Config config = getConfig(mchCode, wechatPayConfig);
        // 构建请求客户端
        AppServiceExtension service = new AppServiceExtension.Builder().config(config).build();
        QueryOrderByOutTradeNoRequest queryOrderByOutTradeNoRequest = new QueryOrderByOutTradeNoRequest();
        queryOrderByOutTradeNoRequest.setMchid(wechatPayConfig.getMchId());
        queryOrderByOutTradeNoRequest.setOutTradeNo(orderNo);
        com.wechat.pay.java.service.payments.model.Transaction transaction = service.queryOrderByOutTradeNo(queryOrderByOutTradeNoRequest);
        if(transaction == null){
            throw new RuntimeException("获取微信订单信息失败！");
        }
        // 定义对应的时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
        // 解析字符串为 ZonedDateTime
        PaymentCallBackResult paymentCallBackResult = new PaymentCallBackResult();
        paymentCallBackResult.setOrderNo(transaction.getOutTradeNo());
        paymentCallBackResult.setNotifyTime(LocalDateTime.now());
        switch (transaction.getTradeState()){
            case SUCCESS:
                paymentCallBackResult.setStatus(PayStatus.SUCCESS);
                ZonedDateTime dateTime = ZonedDateTime.parse(transaction.getSuccessTime(), formatter);
                paymentCallBackResult.setCompletedTime(dateTime.toLocalDateTime());
                paymentCallBackResult.setTransactionId(transaction.getTransactionId());
                paymentCallBackResult.setOpenId(transaction.getPayer().getOpenid());
                break;
            case CLOSED:
                paymentCallBackResult.setStatus(PayStatus.TIMEOUT);
                break;
            default:
                paymentCallBackResult.setStatus(PayStatus.FAIL);
        }
        paymentCallBackResult.setCode(transaction.getTradeState().toString());
        paymentCallBackResult.setMessage(transaction.getTradeStateDesc());
        return paymentCallBackResult;
    }

    @Override
    public RefundResultServiceVO refund(RefundServiceDTO refundDTO, RefundOrder refundOrder) {
        RefundResultServiceVO result = new RefundResultServiceVO();
        result.setRefundOrderNo(refundOrder.getRefundOrderNo());
        result.setRefundBizOrderNo(refundOrder.getRefundBizOrderNo());
        WechatPayConfig payConfig = wechatPayConfigService.lambdaQuery().eq(WechatPayConfig::getMchCode, refundOrder.getMchCode()).one();
        Config config = getConfig(refundOrder.getMchCode(), payConfig);
        RefundService refundService = new RefundService.Builder().config(config).build();
        // 创建退款请求
        CreateRequest request = new CreateRequest();
        request.setNotifyUrl((StrUtil.format(refundNotifyUrl, refundOrder.getMchCode(),refundOrder.getPayChannel().toString())));
        request.setOutTradeNo(refundOrder.getOrderNo());
        request.setOutRefundNo(refundOrder.getRefundOrderNo());
        request.setReason("正常退款");
        AmountReq amountRefund = new AmountReq();
        amountRefund.setRefund(refundOrder.getRefundAmount());
        amountRefund.setTotal(refundOrder.getRefundAmount());
        amountRefund.setCurrency("CNY");
        request.setAmount(amountRefund);
        // 发起退款
        Refund refund = refundService.create(request);
        // 定义对应的时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;

        log.info("微信退款结果：{}", JSONUtil.toJsonStr(refund));
        switch (refund.getStatus()){
            case SUCCESS:
                result.setStatus(PayStatus.SUCCESS);
                // 解析字符串为 ZonedDateTime
                ZonedDateTime dateTime = ZonedDateTime.parse(refund.getSuccessTime(), formatter);
                result.setTransactionId(refund.getRefundId());
                result.setCompletedTime(dateTime.toLocalDateTime());
                break;
            case PROCESSING:
                result.setStatus(PayStatus.PENDING);
                break;
            default:
                result.setStatus(PayStatus.FAIL);
       }
        return result;
    }

    @Override
    public RefundResultServiceVO refundQueryOrder(RefundOrder refundOrder) {
        RefundResultServiceVO result = new RefundResultServiceVO();
        result.setRefundOrderNo(refundOrder.getRefundOrderNo());
        result.setRefundBizOrderNo(refundOrder.getRefundBizOrderNo());
        WechatPayConfig payConfig = wechatPayConfigService.lambdaQuery().eq(WechatPayConfig::getMchCode, refundOrder.getMchCode()).one();
        Config config = getConfig(refundOrder.getMchCode(), payConfig);
        RefundService refundService = new RefundService.Builder().config(config).build();
        QueryByOutRefundNoRequest queryByOutRefundNoRequest = new QueryByOutRefundNoRequest();
        queryByOutRefundNoRequest.setOutRefundNo(refundOrder.getRefundOrderNo());
        // 查询退款订单
        Refund refund = refundService.queryByOutRefundNo(queryByOutRefundNoRequest);
        log.info("微信退款查询结果：{}", JSONUtil.toJsonStr(refund));
        // 定义对应的时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
        // 解析字符串为 ZonedDateTime
        ZonedDateTime dateTime = ZonedDateTime.parse(refund.getSuccessTime(), formatter);
        switch (refund.getStatus()){
            case SUCCESS:
                result.setStatus(PayStatus.SUCCESS);
                result.setTransactionId(refund.getRefundId());
                result.setCompletedTime(dateTime.toLocalDateTime());
                break;
            case PROCESSING:
                result.setStatus(PayStatus.PENDING);
                break;
            default:
                result.setStatus(PayStatus.FAIL);
        }
        return result;
    }

    @Override
    public void close(PayOrder payOrder) {
        WechatPayConfig payConfig = wechatPayConfigService.lambdaQuery().eq(WechatPayConfig::getMchCode, payOrder.getMchCode()).one();
        Config config = getConfig(payOrder.getMchCode(), payConfig);
        AppServiceExtension service = new AppServiceExtension.Builder().config(config).build();
        CloseOrderRequest closeOrderRequest = new CloseOrderRequest();
        closeOrderRequest.setMchid(payConfig.getMchId());
        closeOrderRequest.setOutTradeNo(payOrder.getOrderNo());
        service.closeOrder(closeOrderRequest);
        PaymentCallBackResult paymentCallBackResult = queryOrder(payOrder.getMchCode(), payOrder.getOrderNo());
        if(!paymentCallBackResult.getStatus().equals(PayStatus.TIMEOUT)){
            throw new RuntimeException("微信订单关闭失败！");
        }
    }


    /**
     * 读取请求数据流
     *
     * @param request
     * @return
     */
    private String getRequestBody(HttpServletRequest request) {
        StringBuffer sb = new StringBuffer();
        try (ServletInputStream inputStream = request.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        ) {
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            log.error("读取数据流异常:{}", e.getMessage(),e);
        }
        return sb.toString();
    }


    /**
     * app 请求
     * @param request
     * @param wechatPayConfig
     * @return
     */
    private String appRequest(PrepayRequest request,WechatPayConfig wechatPayConfig,String mchCode){
        // 获取请求配置
        Config config = getConfig(mchCode, wechatPayConfig);
        // 构建请求客户端
        AppServiceExtension service = new AppServiceExtension.Builder().config(config).build();
        PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
        log.info("微信支付响应：{}", JSONUtil.toJsonStr(response));
        return JSONUtil.toJsonStr(response);
    }


    /**
     * 获取请求配置
     * @param mchCode
     * @param wechatPayConfig
     * @return
     */
    private Config getConfig(String mchCode,WechatPayConfig wechatPayConfig){
        Config config = configMap.get(mchCode);
        if(config == null){
            Config wechatConfig = new RSAAutoCertificateConfig.Builder().merchantId(wechatPayConfig.getMchId())
                    .privateKey(wechatPayConfig.getMerchantPrivateKey())
                    .apiV3Key(wechatPayConfig.getApiV3Key())
                    .merchantSerialNumber(wechatPayConfig.getMerchantSerialNumber())
                    .build();
            configMap.put(mchCode,wechatConfig);
        }
        return configMap.get(mchCode);
    }

    /**
     * 获取验签配置
     * @param mchCode
     * @param wechatPayConfig
     * @return
     */
    private NotificationConfig getNotifyConfig(String mchCode,WechatPayConfig wechatPayConfig){
        NotificationConfig config = notifyMap.get(mchCode);
        if(config == null){
            NotificationConfig notificationConfig = new RSAAutoCertificateConfig.Builder()
                    .privateKey(wechatPayConfig.getMerchantPrivateKey())
                    .merchantId(wechatPayConfig.getMchId())
                    .apiV3Key(wechatPayConfig.getApiV3Key())
                    .merchantSerialNumber(wechatPayConfig.getMerchantSerialNumber())
                    .build();
            notifyMap.put(mchCode,notificationConfig);
        }
        return notifyMap.get(mchCode);
    }

    /**
     * 组装app请求参数
     * @param payServiceDTO
     * @param payOrder
     * @param wechatPayConfig
     * @return
     */
    private PrepayRequest getPrepayRequest(PayServiceDTO payServiceDTO, PayOrder payOrder,WechatPayConfig wechatPayConfig){
        PrepayRequest prepayRequest = new PrepayRequest();
        prepayRequest.setAppid(wechatPayConfig.getAppId());
        prepayRequest.setMchid(wechatPayConfig.getMchId());
        prepayRequest.setDescription(payServiceDTO.getDescription());
        prepayRequest.setOutTradeNo(payOrder.getOrderNo());
        if(payServiceDTO.getTimeExpire() != null){
            // 格式转换
            ZonedDateTime zonedDateTime = payServiceDTO.getTimeExpire().atZone(ZoneOffset.ofHours(8)); // 以+08:00时区为例
            // 定义目标格式：yyyy-MM-DDTHH:mm:ss+TIMEZONE
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX");
            // 格式化为指定格式的字符串
            String formattedDate = zonedDateTime.format(formatter);
            prepayRequest.setTimeExpire(formattedDate);
        }
        Amount amount = new Amount();
        amount.setTotal(payServiceDTO.getTotalAmount().intValue());
        prepayRequest.setAmount(amount);
        prepayRequest.setNotifyUrl(StrUtil.format(notifyUrl, payServiceDTO.getMchCode(),payServiceDTO.getPayMethod().getPayChannel().toString()));
        return prepayRequest;

    }







}
